#include <stdio.h>

#include <mezza/sdate.h>
#include <mezza/int.h>

int mz_sdate_to_string(char *dest, enum mz_sdate_fmt format, const mz_sdate_t* const date) {
    switch(format) {
        case DD_MM_YYYY:
            sprintf(dest, "%02d/%02d/%04d", date->day, date->month, date->year );
            break;
        case MM_DD_YYYY:
            sprintf(dest, "%02d/%02d/%04d", date->month, date->day, date->year );
            break;
        default:
            return 1;
    }
    return 0;
}

int mz_sdate_from_string(const char *src, enum mz_sdate_fmt format, mz_sdate_t *date) {
    int day, month, year;
    mz_sdate_t date_local;
    switch(format) {
        case DD_MM_YYYY:
            sscanf(src, "%02d/%02d/%04d", &day, &month, &year );
            break;
        case MM_DD_YYYY:
            sscanf(src, "%02d/%02d/%04d", &month, &day, &year );
            break;
        default:
            return 1;
    }
    date_local.day = day;
    date_local.month = month;
    date_local.year = year;

    if(mz_sdate_validate(&date_local)) {
        return 2;
    }
    *date = date_local;
    return 0;
}

int mz_sdate_cmp(const mz_sdate_t* const date1, const mz_sdate_t* const date2) {
    if(date1->year > date2->year) return 1;
    else if(date1->year < date2->year) return -1;
    else {
        if(date1->month > date2->month) return 1;
        else if(date1->month < date2->month) return -1;
        else {
            if(date1->day > date2->day) return 1;
            else if(date1->day < date2->day) return -1;
        }
    }
    return 0;
}

int mz_sdate_is_leapyear(int year) {

    /*
    if(year%4) return 0;
    if(year%100) {
        if(year%400) return 0;
    }
    return 1;
    */

    /* fonte:
        http://pt.wikipedia.org/wiki/Ano_bissexto#Algoritmo_de_determina.C3.A7.C3.A3o */
    if ( ( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 )
        return 1;
    return 0;
}

int mz_sdate_month_ndays(int year, int month, int *ndays) {
    switch(month) {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            *ndays = 31;
            break;
        case 2:
            *ndays = 28 + mz_sdate_is_leapyear(year);
            break;
        case 4:
        case 6:
        case 9:
        case 11:
            *ndays = 30;
            break;
        default:
            return 1;
    }
    return 0;
}

int mz_sdate_diff(const mz_sdate_t* const date1, const mz_sdate_t* const date2) {
    int ret;
    int i_ano;
    int i_mes;
    int i_dia;
    int ndays;
    int nmonths;
    mz_sdate_t date1_local;
    mz_sdate_t date2_local;

    date1_local = *date1;
    date2_local = *date2;

    if(0 > mz_sdate_cmp(&date1_local, &date2_local)) {
        mz_sdate_swap(&date1_local, &date2_local);
    }

    if(0 < mz_sdate_cmp(&date1_local, &date2_local)) {
        for(ret = 0, i_ano = date2_local.year; i_ano <= date1_local.year; i_ano++) {
            if(i_ano == date1_local.year)
                nmonths = date1_local.month;
            else
                nmonths = 12;

            if(i_ano == date2_local.year)
                i_mes = date2_local.month;
            else
                i_mes = 1;

            for(; i_mes<=nmonths; i_mes++) {
                if((i_ano == date1_local.year) && (i_mes == date1_local.month))
                    ndays = date1_local.day;
                else
                    mz_sdate_month_ndays(i_ano, i_mes, &ndays);

                if((i_ano == date2_local.year) && (i_mes == date2_local.month))
                    i_dia = date2_local.day+1;
                else
                    i_dia = 1;

                for(; i_dia <= ndays; i_dia++) {
                    ret++;
                }
            }
        }
    }

    return ret;
}

void mz_sdate_swap(mz_sdate_t *date1, mz_sdate_t *date2) {

    SWAP_INT(date1->day, date2->day)
    SWAP_INT(date1->month, date2->month)
    SWAP_INT(date1->year, date2->year)
}

int mz_sdate_validate(const mz_sdate_t* const date1) {
    int ndays;

    if(date1->year < 1 || date1->year > SDATE_YEAR_MAXLIMIT) return 1;
    if(date1->month < 1 || date1->month > 12) return 2;
    mz_sdate_month_ndays(date1->year, date1->month, &ndays);
    if(date1->day < 1 || date1->day > ndays) return 3;
    return 0;
}
